import fs from 'fs'
import pLimit from 'p-limit'
import path from 'path'
import cliProgress from 'cli-progress'
import initCycleTLS, { CycleTLSClient } from 'cycletls'
import { CrawlerFileUtils, getRandomIP, type Config, getRandomUserAgent, getRandomAcceptEncoding, getRandomAcceptLanguage } from '../../utils/baseUtils.js'
import axios from 'axios'

// pixabay 配置文件类型
type PixabayConfig = Config & {
  // 请求的 apikeys ，目前注册了两个，如果被限制需要再注册
  apiKeys: string[]
  // 配置信息，具体参考 https://pixabay.com/api/docs/
  params: {
    // 搜索语言，建议英文
    lang: 'en' | 'zh'
    // 图片类型。 images 包括 photo 和 illustration
    image_type: 'images' | 'photos' | 'illustrations'
    // 最多爬取几页，默认10页，每页100张图片
    page_limit: number
  }
  proxy: {
    enable: boolean // 是否启用代理
    type: 'http' | 'https' | 'socks5' // 目前只支持 http
    host: string
    port: number
    username?: string
    password?: string
  }
}

type SearchRes = {
  page: {
    page: number // 当前页数
    pages: number // 总页数
    perPage: number // 每页数量，默认100
    total: number // 总数量
    results: [
      {
        id: number // 图片id
        height: number // 图片高度
        width: number // 图片宽度
        mediaDescriptiveType: 'photo' | 'illustration' // 图片类型
        isAiGenerated: boolean // 是否是 AI 生成
        name: string // 图片的 TAG
        sources: {
          '1x': string // 图片地址 https://cdn.pixabay.com/photo/2016/11/29/07/36/ice-1868139_640.jpg
        }
      }
    ]
  }
}

type Image = {
  id: number // 图片id
  fileName: string // 文件名
  url: string // 图片地址
  height: number // 图片高度
  width: number // 图片宽度
  type: 'photo' | 'illustration' // 图片类型
  tag: string // 图片的 TAG，用, 拆分
  isAiGenerated: boolean // 是否是 AI 生成
}

// 创建文件工具实例
export const fileUtils = new CrawlerFileUtils<PixabayConfig>(import.meta.url)

// ====================== 请求配置相关 ======================
// 一定会有的 header
const getCommonHeaders = () => ({
  'x-fetch-bootstrap': '1',
  'x-csrftoken': 'q6rcqxNuYHZlQOnDkzF9H4Vdh4RNeJxC',
  cookie:
    'is_human=1; anonymous_user_id=bccce25efd7847c7918d01951b80a840; dwf_user_generated_playlists=False; g_rated=off; OptanonAlertBoxClosed=2024-08-02T01:57:13.136Z; dwf_show_mdp_getty_brand_ad=False; cf_clearance=a3kLY_rmDPQ10klj_Oyk8_Qf4afvlT38BBo8vvWkC_I-1722934387-1.0.1.1-1j_nht2X4xPwqMYOdDFcrYMiu9ilHq2rwdwlPCpwshOP1mqYFEpNsZbc4R66rcmXYDPoA1wBMqPJ9pG4qJxgow; csrftoken=q6rcqxNuYHZlQOnDkzF9H4Vdh4RNeJxC; user_id=45233965; sessionid=.eJxVzEsOwiAUheG9MDYNBW4LboYA95piWzE84sC4d7EDo-P_nO_JrGt1sa1QthHZmSkQUpoJ2Ok3eRdWun36PacrhTq0GrcyhFZq2o_hEI_pze1kU7a0u7h9f3_Y4srSJfBGaw8Snbwgzoq08iMHHD2M5I3wKLRDrnoNYlYTInAkDaidVtxQRyuVGlJaI3XvkfJKyF5vf3NHQA:1sgEge:L4yBhlkjnSord3SaKaQPCiEg0fOYmNrMJVhIPpoCj8M; _sp_ses.aded=*; lang=en; client_width=1897; OptanonConsent=isGpcEnabled=0&datestamp=Tue+Aug+20+2024+10%3A57%3A35+GMT%2B0800+(%E4%B8%AD%E5%9B%BD%E6%A0%87%E5%87%86%E6%97%B6%E9%97%B4)&version=202401.2.0&browserGpcFlag=0&isIABGlobal=false&hosts=&consentId=5bd3db82-2d8a-42b5-aa48-d4f28feb5a3a&interactionCount=2&landingPath=NotLandingPage&groups=C0001%3A1%2CC0002%3A1%2CC0003%3A1%2CC0004%3A1&AwaitingReconsent=false&geolocation=HK%3B; _sp_id.aded=8c40cc72-1ba3-4067-9a91-5e3306f4afc7.1722411008.19.1724122656.1724047613.f49d8972-86c3-4655-b76f-194087d77483.0a3fd9a0-ec71-4b46-a6b7-cafdd77845f9.57e2115e-ea7f-4216-9604-878e1e9ec0c2.1724121215270.33; __cf_bm=X5o7pe5y0o3RDiSKA7zKfu.9r2EdAk0JPlPGWgP3NY0-1724122660-1.0.1.1-DFXPZXzHBvFVOiaPEaXn2fhq6y.IxJ7zq0xxr9Rffr5Ka6GkpXD.rPP9qH8IKauDnnVhkLoLx7b6Pc7dOnVpAA',
  'user-agent': getRandomUserAgent(),
  'X-Forwarded-For': getRandomIP(),
})

// 可以随机增加的 header 头
const getAddableHeaders = () => ({
  Referer: 'https://pixabay.com/',
  accept: 'application/json',
  'accept-encoding': getRandomAcceptEncoding(),
  'accept-language': getRandomAcceptLanguage(),
})

// 随机 header 头
function getRandomHeaders() {
  const headers = { ...getCommonHeaders() } as Record<string, string>
  const addableHeaders = getAddableHeaders() as Record<string, string>
  const addableKeys = Object.keys(addableHeaders)
  addableKeys.forEach((key) => {
    if (Math.random() > 0.5) {
      headers[key] = addableHeaders[key]
    }
  })
  return headers
}

const config: { ja3?: string; proxy?: string; timeout?: number } = {
  // ja3: '771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,65037-0-17513-35-65281-27-43-51-10-45-16-5-11-18-23-13,25497-29-23-24,0',
  ja3: '771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,17513-45-16-13-51-65037-10-18-11-65281-23-5-35-0-43-27,25497-29-23-24,0',
  proxy: `${fileUtils.config.proxy.type}://${fileUtils.config.proxy.username ?  (fileUtils.config.proxy.username + ':') : ''}${fileUtils.config.proxy.password || ''}@${fileUtils.config.proxy.host}:${fileUtils.config.proxy.port}`,
  timeout: 60,
}

if (!fileUtils.config.proxy.enable) {
  delete config.proxy
}

// ========================= TLS单例 =========================
let cycleTLS: undefined | CycleTLSClient = undefined

// 初始化 TLS
async function initTLS() {
  if (cycleTLS) return cycleTLS

  cycleTLS = await initCycleTLS({
    timeout: 10000,
  })
  return cycleTLS
}

// 退出 TLS
export async function exitTLS() {
  if (cycleTLS) {
    await cycleTLS.exit()
    cycleTLS = undefined
  }
}

// 假退出
export function fakeExitTLS() {
  if (cycleTLS) {
    cycleTLS = undefined
  }
}

// ========================= 接口实现 ===========================
// searchBykeyword('编辑精选', 5905)
// 根据关键词搜索图片
export async function searchBykeyword(keyword: string | '编辑精选', page: number, retryTimes: number = 2) {
  const cycleTLS = await initTLS()

  // 获取请求url
  const { image_type, lang } = fileUtils.config.params
  let url: string
  if (keyword === '编辑精选') {
    url = `https://pixabay.com${lang === 'en' ? '' : '/zh'}/${image_type}/search/?order=ec&pagi=${page}`
  } else {
    url = `https://pixabay.com${lang === 'en' ? '' : '/zh'}/${image_type}/search/${keyword}/?pagi=${page}`
  }

  const res = await Promise.race([
    cycleTLS.get(url, {
      ...config,
      headers: getRandomHeaders(),
    }),
    // 增加超时超时处理
    new Promise<never>((_, reject) => setTimeout(() => reject(new Error()), 20000)),
  ])

  // 错误重试
  if (res.status !== 200) {
    if (retryTimes >= 0) {
      return await searchBykeyword(keyword, page, retryTimes - 1)
    } else {
      throw new Error(`关键词 ${keyword} 的第 ${page} 页请求错误！ ${url}`)
    }
  }

  // 将图片信息记录到 json 文件
  console.log('✨ 正在将图片信息记录到 json 文件中...')
  const infoFilePath = path.resolve(fileUtils.__dirname, 'imgs-info.json')
  // 如果没有文件，则创建文件
  if (!fs.existsSync(infoFilePath)) {
    fs.writeFileSync(infoFilePath, '{}')
  }
  const data = fs.readFileSync(infoFilePath, 'utf-8')
  const jsonData = JSON.parse(data)

  const body = res.body as SearchRes
  const imgs = body.page.results.map((item) => {
    // 后缀解析
    const splitRes = item.sources['1x'].split('/')
    const suffixs = splitRes[splitRes.length - 1].split('.')
    const fileName = suffixs[0].split('_')[0] + '.' + suffixs[1]
    const imgObj = {
      id: item.id,
      fileName,
      height: item.height,
      type: item.mediaDescriptiveType,
      width: item.width,
      tag: item.name,
      isAiGenerated: item.isAiGenerated,
      // url: `https://pixabay.com/images/download/${fileName}`,
      url: `https://pixabay.com/zh/images/download/${fileName}`,
    } as Image

    jsonData[imgObj.fileName] = imgObj

    return imgObj
  })

  fs.writeFileSync(infoFilePath, JSON.stringify(jsonData, null, 2))

  return {
    total: body.page.total,
    page: body.page.page,
    pages: body.page.pages,
    perPage: body.page.perPage,
    imgs,
  }
}

// 下载图片到本地
export async function downloadImgs(keyword: string | '编辑精选', imgs: Image[]) {
  const dir = path.resolve(fileUtils.outDirPath, keyword, fileUtils.config.params.image_type)
  fs.mkdirSync(dir, { recursive: true }) //确保目录存在

  // 初始化进度条
  const progressBar = new cliProgress.SingleBar({}, cliProgress.Presets.rect)
  progressBar.start(imgs.length, 0)

  // 限制下载并发
  const limit = pLimit(fileUtils.config.concurrency)
  const promises = imgs.map(async (img, index) => {
    return limit(async () => {
      const filePath = path.resolve(dir, img.fileName)
      await downloadImg(img.url, filePath)
      progressBar.increment() // 更新进度条
    })
  })

  await Promise.all(promises)
  // await Promise.allSettled(promises)
  progressBar.stop() // 停止进度条
}

// 测试用例
// downloadImg('https://pixabay.com/zh/images/download/iceland-2448121.jpg', 'D:/Front_End_Work/cs-crawler/src/pixabay-com/iceland-2448121.jpg')

async function downloadImg(imgUrl: string, filePath: string, retryTimes: number = 2) {
  // 如果文件已经存在，则直接返回
  if (fs.existsSync(filePath)) {
    return
  }

  const cycleTLS = await initTLS()

  try {
    const res = await Promise.race([
      cycleTLS.get(imgUrl, {
        ...config,
        headers: getRandomHeaders(),
      }),
      // 增加超时超时处理
      new Promise<never>((_, reject) => setTimeout(() => reject(new Error()), 40000)),
    ])
    if (res.status == 404){
      return
    }
    // 说明到了登入页，而非是图片，cookie过期
    if (!res.headers['Content-Type'].includes('image')) {
      throw new Error(`❌ 下载接口返回非图片！[body]: ${res.body}`)
      // pushToWeChat(`pixabay.com\ncookie可能过期需要人工介入！😭\n@周辉 @邓雄威 来人！救虫啊！`)
    }

    writeImage(filePath, res.body)
  } catch (error) {
    if (retryTimes >= 0) {
      await downloadImg(imgUrl, filePath, retryTimes - 1)
    } else {
      throw new Error(`⚠️ 下载图片超时：${imgUrl}`, { cause: error })
    }
  }
}

// 写图片 https://github.com/Danny-Dasilva/CycleTLS?tab=readme-ov-file#javascript-image-write-to-file
const writeImage = (filename: fs.PathLike, data: any) => {
  let writeStream = fs.createWriteStream(filename)

  // write some data with a base64 encoding
  writeStream.write(data, 'base64')
  writeStream.end()
}

// 企业微信推送
export function pushToWeChat(content: string) {
  axios.post(
    'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=65ecab09-ed93-4417-859e-fce5ed1b68db',
    {
      msgtype: 'text',
      text: { content },
    },
    { headers: { 'Content-Type': 'application/json' } }
  )
}
